import csv
import multiprocessing
import os
import random
import sqlite3 as lite
from dataclasses import dataclass, field
from datetime import timedelta
import datetime
from enum import Enum
from itertools import product
from typing import Callable
import copy
import time

import pandas as pd

from .configpath import sqlite_stra_rslt, sqlite_stra_conf, csv_file_path
from .constant import Direction, Offset, Interval, IntervalType, DirectType, OffsetType

# from BQ.entity.stra_entity import Bar

long_cond_5pv = [[14, 1], [17, 1], [20, 1], [14, 2], [17, 2], [20, 2], [13, 5], [19, 6],
                 [11, 8], [12, 8], [15, 8], [18, 8], [11, 9], [12, 9], [18, 9],
                 [4, 13], [10, 13], [4, 14], [7, 14], [10, 14], [3, 17], [6, 17],
                 [9, 17], [1, 20], [2, 20], [5, 20], [8, 20]]
short_cond_5pv = [[20, 3], [14, 4], [17, 4], [20, 4], [13, 7], [16, 7], [19, 7],
                  [11, 10], [12, 10], [15, 10], [18, 10], [4, 11], [7, 11], [10, 11],
                  [4, 12], [7, 12], [10, 12], [3, 15], [9, 15], [9, 16], [1, 18],
                  [2, 18], [5, 18], [8, 18], [1, 19], [8, 19]]

long_cond_4pv = [1, 2, 3, 5, 6, 8, 9, 14, 17, 20]
short_cond_4pv = [11, 12, 13, 15, 16, 18, 19, 4, 7, 10]
long_stop_cond_zc = [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]  # 当开多时的 zig_cycle 在此列表中时要设置止损
short_stop_cond_zc = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]  # 当开空时的 zig_cycle 在此列表中时要设置止损
long_stop_cond_pv = [1]  # 当峰被发现时开多要止损
short_stop_cond_pv = [-1]  # 当谷被发现时开空要止损


def save_signals(table_name, data: pd.DataFrame = None):
    """"保存信号到sqlite db 中"""
    with lite.connect(sqlite_stra_rslt) as cnn:
        data.to_sql(table_name, cnn, if_exists='replace')


def save_stra_conf(stra, conf=None):
    """保存策略的配置、优化过的参数、多空开平仓条件等，由具体策略在参数优化后调用"""
    create_sql = """
                create table if not exists [stra_conf](
                [id] varchar(100) not null  primary key,
                [class_name] text,
                [symbol] varchar(100),
                [period] int,
                [params] text,
                [conf] text,
                [data_datetime] text,
                update_datetime TIMESTAMP DEFAULT (datetime('now', 'localtime')),
                [vars] text null                
                );"""
    if not conf:
        conf = stra.conf
    params = stra.get_parameters()
    vars = stra.get_vars()  # 若没有变量，vars={}

    data_datetime = {}
    data_len = getattr(stra.stra_engine, 'init_data_len', 0)
    if data_len == 0:
        data_datetime['start_dt'] = getattr(stra.stra_engine, 'start_dt', '')
        data_datetime['end_dt'] = getattr(stra.stra_engine, 'end_dt', '')
    else:
        data_datetime['data_len'] = data_len

    with lite.connect(sqlite_stra_conf) as cnn:
        stra_id = '_'.join([stra.__class__.__name__, stra.symbol, str(stra.period)])
        sql = f"""
                insert or replace into [stra_conf]([id],[symbol],[period],[params],[conf],[data_datetime])
                values("{stra_id}","{stra.symbol}",{stra.period},"{str(params)}","{str(conf)}",
                "{str(data_datetime)}")"""
        if vars:
            sql = f"""
                    insert or replace into [stra_conf]([id],[symbol],[period],[params],[conf],[data_datetime],[vars])
                    values("{stra_id}","{stra.symbol}",{stra.period},"{str(params)}","{str(conf)}",
                    "{str(data_datetime)}","{str(vars)}")"""
        cnn.executescript(create_sql)
        cnn.commit()
        cnn.execute(sql)
        stra.conf_saved = True


def load_conf(stra):
    """读取保存的配置及参数，由具体策略调用"""
    with lite.connect(sqlite_stra_conf) as cnn:
        sql = f"""select [id],[symbol],[period],[params],[conf] 
            from stra_conf 
            where [symbol]='{stra.symbol}' and [period]={stra.period} 
            and class_name ='{stra.__class__.__name__}')"""
        data = pd.read_sql(sql, cnn)
        if data is not None and not data.empty:
            return data

        return None


def save_stra_signal_csv(stra_name, params, dt, symbol, offset, direction, price, vol, bssc=0, other=''):
    stra_name = os.path.join(csv_file_path, stra_name + '.csv')
    neen_write_header = not os.path.exists(stra_name)
    with open(stra_name, 'a') as csv_file:
        header = ['params', 'datetime', 'symbol', 'direction',
                  'offset', 'price', 'volume', 'bssc', 'other']
        csv_writer = csv.DictWriter(csv_file, header)
        if neen_write_header:
            csv_writer.writeheader()
        data = {'params': params, 'datetime': dt, 'symbol': symbol,
                'direction': direction, 'offset': offset, 'price': price,
                'volume': vol, 'bssc': bssc, 'other': other}
        csv_writer.writerow(data)


class PriceType(Enum):
    """限价"""
    LIMIT = 0
    """市价"""
    MARKET = 1
    """下一市价"""
    MARKETA = 2
    """开盘价"""
    OPEN = 3
    """收盘价"""
    CLOSE = 4
    """下一市价"""
    BEST = 5
    """下一市价"""
    WORST = 6


class StopOrderStatus(Enum):
    WAITING = "等待中"
    CANCELLED = "已撤销"
    TRIGGERED = "已触发"


class EngineType(Enum):
    LIVE = "实盘"
    BACKTESTING = "回测"
    TEST = "测试"  # 利用实盘行情数据，运行策略，保存策略信号


class BacktestingMode(Enum):
    BAR = 1
    TICK = 0


TickOrBar = BacktestingMode


@dataclass
class StopOrder:
    vt_symbol: str
    direction: Direction
    offset: Offset
    price: float
    volume: float
    stop_orderid: str
    strategy_name: str
    lock: bool = False
    vt_orderids: list = field(default_factory=list)
    status: StopOrderStatus = StopOrderStatus.WAITING


EVENT_CTA_LOG = "eCtaLog."
EVENT_STRATEGY = "eStrategy."
EVENT_STOP_ORDER = "eStopOrder."

INTERVAL_DELTA_MAP = {
    Interval.TICK: timedelta(milliseconds=500),
    Interval.MINUTE: timedelta(minutes=1),
    Interval.HOUR: timedelta(hours=1),
    Interval.DAILY: timedelta(days=1),
}


class OptimizationSetting:
    """
    Setting for runnning optimization.
    """

    def __init__(self):
        """"""
        self.params = {}
        self.target_name = ""

    def add_parameter(
            self, name: str, start: float, end: float = None, step: float = None
    ):
        """"""
        if not end and not step:
            self.params[name] = [start]
            return

        if start >= end:
            print("参数优化起始点必须小于终止点")
            return

        if step <= 0:
            print("参数优化步进必须大于0")
            return

        value = start
        value_list = []

        while value <= end:
            value_list.append(value)
            value += step

        self.params[name] = value_list

    def set_target(self, target_name: str):
        """"""
        self.target_name = target_name

    def generate_setting(self):
        """"""
        keys = self.params.keys()
        values = self.params.values()
        products = list(product(*values))

        settings = []
        for p in products:
            setting = dict(zip(keys, p))
            settings.append(setting)

        return settings

    def generate_setting_ga(self):
        """为遗传算法准备参数设置"""
        settings_ga = []
        settings = self.generate_setting()
        for d in settings:
            param = [tuple(i) for i in d.items()]
            settings_ga.append(param)
        return settings_ga


def run_optimization(self, optimization_setting: OptimizationSetting, output=True, long_short='b'):
    """"""
    # Get optimization setting and target
    settings = optimization_setting.generate_setting()
    target_name = optimization_setting.target_name

    if not settings:
        self.output("优化参数组合为空，请检查")
        return

    if not target_name:
        self.output("优化目标未设置，请检查")
        return

    # Use multiprocessing pool for running backtesting with different setting
    # Force to use spawn method to create new process (instead of fork on Linux)
    ctx = multiprocessing.get_context("spawn")
    pool = ctx.Pool(multiprocessing.cpu_count())

    results = []
    for setting in settings:
        result = (pool.apply_async(optimize, (
            target_name,
            self.strategy_class,
            setting,
            self.vt_symbol,
            self.interval,
            self.start,
            self.rate,
            self.slippage,
            self.size,
            self.pricetick,
            self.capital,
            self.end,
            self.mode,
            self.inverse
        )))
        results.append(result)

    pool.close_price()
    pool.join()

    # Sort results and output
    result_values = [result.get() for result in results]
    result_values.sort(reverse=True, key=lambda result: result[1])

    if output:
        for value in result_values:
            msg = f"参数：{value[0]}, 目标：{value[1]}"
            self.output(msg)

    return result_values


def run_ga_optimization(self, optimization_setting: OptimizationSetting,
                        population_size=100, ngen_size=30, output=True, long_short='b'):
    """"""
    # Get optimization setting and target
    settings = optimization_setting.generate_setting_ga()
    target_name = optimization_setting.target_name

    if not settings:
        self.output("优化参数组合为空，请检查")
        return

    if not target_name:
        self.output("优化目标未设置，请检查")
        return

    # Define parameter generation function
    def generate_parameter():
        """"""
        return random.choice(settings)

    def mutate_individual(individual, indpb):
        """"""
        size = len(individual)
        paramlist = generate_parameter()
        for i in range(size):
            if random.random() < indpb:
                individual[i] = paramlist[i]
        return individual,

    # Create ga object function
    global ga_target_name
    global ga_strategy_class
    global ga_setting
    global ga_vt_symbol
    global ga_interval
    global ga_start
    global ga_rate
    global ga_slippage
    global ga_size
    global ga_pricetick
    global ga_capital
    global ga_end
    global ga_mode
    global ga_inverse


def optimize(func: Callable, target_setting, **params):
    """"""


class StraSignal:
    def __init__(self):
        self.id = ''
        self.symbol = ''
        self.ocls = 0
        self.price = 0
        self.stop_price = 0
        self.remark = None


class OrderItem(object):
    """策略信号"""

    def __init__(self):
        """Constructor"""
        '''合约'''
        self.Instrument = ''
        '''合约'''
        '''时间 yyyyMMdd HH:mm:ss'''
        self.DateTime = time.strftime('%Y%m%d %H:%Mm:%S', time.localtime(time.time()))
        '''时间 yyyyMMdd HH:mm:ss'''
        '''买卖'''
        self.Direction = DirectType.Buy
        '''买卖'''
        '''开平'''
        self.Offset = OffsetType.Open
        '''开平'''
        '''价格'''
        self.Price = 0.0
        '''价格'''
        '''数量'''
        self.Volume = 0
        '''数量'''
        '''备注'''
        self.Remark = ''
        '''备注'''
        '''关联的开仓指令'''
        self.RelationOpenOrders = []
        '''关联的开仓指令'''
        '''开仓均价-空'''
        self.AvgEntryPriceShort = 0.0
        '''开仓均价-空'''
        '''开仓均价-多'''
        self.AvgEntryPriceLong = 0.0
        '''开仓均价-多'''
        '''持仓-多'''
        self.PositionLong = 0
        '''持仓-多'''
        '''持仓-空'''
        self.PositionShort = 0
        '''持仓-空'''
        '''开仓时间-多'''
        self.EntryDateLong = ''
        '''开仓时间-多'''
        '''开仓时间-空'''
        self.EntryDateShort = ''
        '''开仓时间-空'''
        '''开仓价格-多'''
        self.EntryPriceLong = 0.0
        '''开仓价格-多'''
        '''开仓价格-空'''
        self.EntryPriceShort = 0.0
        '''开仓价格-空'''
        '''平仓时间-多'''
        self.ExitDateLong = ''
        '''平仓时间-多'''
        '''平仓时间-空'''
        self.ExitDateShort = ''
        '''平仓时间-空'''
        '''平仓价格-多'''
        self.ExitPriceLong = 0.0
        '''平仓价格-多'''
        '''平仓价格-空'''
        self.ExitPriceShort = 0.0
        '''平仓价格-空'''
        '''最后一次开仓时间-多'''
        self.LastEntryDateLong = ''
        '''最后一次开仓时间-多'''
        '''最后一次开仓时间-空'''
        self.LastEntryDateShort = ''
        '''最后一次开仓时间-空'''
        '''最后一次开仓价格-多'''
        self.LastEntryPriceLong = 0.0
        '''最后一次开仓价格-多'''
        '''最后一次开仓价格-空'''
        self.LastEntryPriceShort = 0.0
        '''最后一次开仓价格-空'''
        '''开仓到当前K线的数量(0开始)-多'''
        self.IndexEntryLong = 0.0
        '''开仓到当前K线的数量(0开始)-多'''
        '''开仓到当前K线的数量(0开始)-空'''
        self.IndexEntryShort = 0.0
        '''开仓到当前K线的数量(0开始)-空'''
        '''最后开仓到当前K线的数量(0开始)-多'''
        self.IndexLastEntryLong = -1
        '''最后开仓到当前K线的数量(0开始)-多'''
        '''最后开仓到当前K线的数量(0开始)-空'''
        self.IndexLastEntryShort = -1
        '''最后开仓到当前K线的数量(0开始)-空'''
        '''平仓到当前K线的数量(0开始)-多'''
        self.IndexExitLong = -1
        '''平仓到当前K线的数量(0开始)-多'''
        '''平仓到当前K线的数量(0开始)-空'''
        self.IndexExitShort = -1
        '''平仓到当前K线的数量(0开始)-空'''
        '''开仓时是否下止损单(0不止损)'''
        self.StopPrice = 0
        '''价格类型'''
        self.PriceType = PriceType.LIMIT

    def __str__(self):
        """"""
        return f'{self.Instrument}, {self.DateTime}, {self.Direction}, {self.Offset}, {self.Price}, {self.Volume}, {self.Remark}'


class Bar(object):
    """K线数据"""

    def __init__(self, dt: str, ins: str, h: float, l: float, o: float, c: float, v: int, i: float,
                 tradingday: str):
        """初始化"""
        """时间
        yyyyMMdd HH:mm:ss"""
        self.D = dt
        """时间
        yyyyMMdd HH:mm:ss"""
        """合约"""
        self.Instrument = ins
        """合约"""
        """交易日"""
        self.Tradingday = tradingday
        """交易日"""
        """最高价"""
        self.H = h
        """最高价"""
        """最低价"""
        self.L = l
        """最低价"""
        """开仓价"""
        self.O = o
        """开仓价"""
        """收盘价"""
        self.C = c
        """收盘价"""
        """成交量"""
        self.V = v
        """成交量"""
        """持仓价"""
        self.I = i
        """持仓价"""
        self._pre_volume = 0

    def __str__(self):
        """"""
        return '{self.D}, {self.H}, {self.L}, {self.O}, {self.C}, {self.V}, {self.I}'.format(
            self=self)


class Tick:
    """分笔数据"""

    def __init__(self):
        """初始化"""
        '''合约'''
        self.Instrument = ''
        '''合约'''
        self.InstrumentID = ''
        self.ExchangeID = ''
        '''最新价'''
        self.LastPrice = 0.0
        '''最新价'''
        '''挂卖价'''
        self.AskPrice1 = 0.0
        '''挂卖价'''
        '''挂买价'''
        self.BidPrice1 = 0.0
        '''挂买价'''
        '''挂卖量'''
        self.AskVolume1 = 0
        '''挂卖量'''
        '''挂买量'''
        self.BidVolume1 = 0
        '''挂买量'''
        '''时间'''
        self.UpdateTime = ''
        '''时间'''
        '''毫秒'''
        self.UpdateMillisec = 0
        '''毫秒'''
        '''成交量'''
        self.Volume = 0
        '''成交量'''
        '''持仓量'''
        self.OpenInterest = 1.0
        '''持仓量'''
        '''均价'''
        self.AveragePrice = 0.0
        '''均价'''
        '''涨板价'''
        self.UpperLimitPrice = 0.0
        '''涨板价'''
        '''跌板价'''
        self.LowerLimitPrice = 0.0
        '''跌板价'''
        '''昨结算价'''
        self.PreSettlementPrice = 0.0
        '''昨结算价'''
        '''昨日持仓'''
        self.PreOpenInterest = 0.0
        '''当日最高价'''
        self.HighestPrice = 0.0
        '''当日最低价'''
        self.LowestPrice = 0.0
        '''当日开盘价'''
        self.OpenPrice = 0.0
        '''当日收盘价'''
        self.ClosePrice = 0.0
        '''昨日收盘价'''
        self.PreClosePrice = 0.0
        '''日期时间'''
        self.DateTime = ''
        self.SettlementPrice = 0.0
        # self.AskPrice2 = 0.0
        # self.BidPrice2 = 0.0
        # self.AskVolume2 = 0
        # self.BidVolume2 = 0
        # self.AskPrice3 = 0.0
        # self.BidPrice3 = 0.0
        # self.AskVolume3 = 0
        # self.BidVolume3 = 0
        # self.AskPrice4 = 0.0
        # self.BidPrice4 = 0.0
        # self.AskVolume4 = 0
        # self.BidVolume4 = 0
        # self.AskPrice5 = 0.0
        # self.BidPrice5 = 0.0
        # self.AskVolume5 = 0
        # self.BidVolume5 = 0
        '''挂买量'''
        self._id = ''

    @property
    def BidPrice(self):
        return self.BidPrice1

    @property
    def BidVolume(self):
        return self.BidVolume1

    @property
    def AskPrice(self):
        return self.AskPrice1

    @property
    def AskVolume(self):
        return self.AskVolume1

    @property
    def ReceivedTime(self):
        '''收到时间'''
        return datetime.datetime.now().strftime('%H:%M:%S.%f')

    # @property
    # def _id(self):
    #     return '_'.join([self.InstrumentID, self.DateTime, self.UpdateMillisec])

    def __str__(self):
        """"""
        return '{self.Instrument}, {self.LastPrice}, {self.AskPrice}, {self.AskVolume}, {self.BidPrice}, {self.BidVolume}, {self.UpdateTime}, {self.Volume}, {self.OpenInterest}, {self.AveragePrice}, {self.UpperLimitPrice}, {self.LowerLimitPrice}, {self.PreOpenInterest}'.format(
            self=self)

    @property
    def __dict__(self):
        return {
            'Instrument': self.Instrument,
            'LastPrice': self.LastPrice,
            'AskPrice': self.AskPrice,
            'AskVolume': self.AskVolume,
            'BidPrice': self.BidPrice,
            'BidVolume': self.BidVolume,
            'UpdateTime': self.UpdateTime,
            'Volume': self.Volume,
            'OpenInterest': self.OpenInterest,
            'AveragePrice': self.AveragePrice,
            'HighestPrice': self.HighestPrice,
            'LowestPrice': self.LowestPrice,

        }


class Data(object):
    """数据类, 数据驱动的策略继承此类"""

    def __init__(self, bar_update=None, stra_onorder=None, tick_update=None, tickorbar=TickOrBar.BAR, data_len=120):
        """初始所有变量"""
        self.bar_update = bar_update
        self.tick_update = tick_update
        self.stra_onorder = stra_onorder
        self.tickorbar = tickorbar
        self.data_len = data_len
        '''保存数据的长度，当Bar时与参数一致，Tick时放大5倍'''
        self.dl = data_len  # if tickorbar==TickOrBar.Bar else data_len*5
        self.ticks = []
        '''每bar只执行一次交易'''
        self.SingleOrderOneBar = False
        '''每bar只执行一 次交易'''
        '''K线序列'''
        self.Bars = []
        '''K线序列'''
        '''合约'''
        self.Instrument = ''
        '''合约'''
        '''周期'''
        self.Interval = 1
        '''周期'''
        '''周期类型'''
        self.IntervalType = IntervalType.Minute
        '''周期类型'''

        '''买卖信号'''
        self.Orders = []
        '''买卖信号'''

        '''指标字典
        策略使用的指标保存在此字典中
        以便管理程序显示和处理'''
        self.IndexDict = {}
        '''指标字典
        策略使用的指标保存在此字典中
        以便管理程序显示和处理'''
        '''时间'''
        self.D = []
        '''时间'''
        '''最高价'''
        self.H = []
        '''最高价'''
        '''最低价'''
        self.L = []
        '''最低价'''
        '''开盘价'''
        self.O = []
        '''开盘价'''
        '''收盘价'''
        self.C = []
        '''收盘价'''
        '''交易量'''
        self.V = []
        '''交易量'''
        '''持仓量'''
        self.I = []
        '''持仓量'''

        self._lastOrder = OrderItem()

    @property
    def AvgEntryPriceShort(self):
        '''开仓均价-空'''
        return self._lastOrder.AvgEntryPriceShort

    @property
    def AvgEntryPriceLong(self):
        '''开仓均价-多'''
        return self._lastOrder.AvgEntryPriceLong

    @property
    def PositionLong(self):
        '''持仓-多'''
        return self._lastOrder.PositionLong

    @property
    def PositionShort(self):
        '''持仓-空'''
        return self._lastOrder.PositionShort

    @property
    def EntryDateLong(self):
        '''开仓时间-多'''
        return self._lastOrder.EntryDateLong

    @property
    def EntryPriceLong(self):
        '''开仓价格-多'''
        return self._lastOrder.EntryPriceLong

    @property
    def ExitDateShort(self):
        '''平仓时间-空'''
        return self._lastOrder.ExitDateShort

    @property
    def ExitPriceShort(self):
        '''平仓价-空'''
        return self._lastOrder.ExitPriceShort

    @property
    def EntryDateShort(self):
        '''开仓时间-空'''
        return self._lastOrder.EntryDateShort

    @property
    def EntryPriceShort(self):
        '''开仓价-空'''
        return self._lastOrder.EntryPriceShort

    @property
    def ExitDateLong(self):
        '''平仓时间-多'''
        return self._lastOrder.ExitDateLong

    @property
    def ExitPriceLong(self):
        '''平仓价-多'''
        return self._lastOrder.ExitPriceLong

    @property
    def LastEntryDateShort(self):
        '''最后开仓时间-空'''
        return self._lastOrder.LastEntryDateShort

    @property
    def LastEntryPriceShort(self):
        '''最后开仓价-空'''
        return self._lastOrder.LastEntryPriceShort

    @property
    def LastEntryDateLong(self):
        '''最后开仓时间-多'''
        return self._lastOrder.LastEntryDateLong

    @property
    def LastEntryPriceLong(self):
        '''最后开仓价-多'''
        return self._lastOrder.LastEntryPriceLong

    @property
    def IndexEntryLong(self):
        '''开仓到当前K线数量-多'''
        return self._lastOrder.IndexEntryLong

    @property
    def IndexEntryShort(self):
        '''开仓到当前K线数量-空'''
        return self._lastOrder.IndexEntryShort

    @property
    def IndexLastEntryLong(self):
        '''最后开仓到当前K线的数量-多'''
        return self._lastOrder.IndexLastEntryLong

    @property
    def IndexLastEntryShort(self):
        '''最后开仓到当前K线的数量-空'''
        return self._lastOrder.IndexLastEntryShort

    @property
    def IndexExitLong(self):
        '''平仓到当前K线数量-多'''
        return self._lastOrder.IndexExitLong

    @property
    def IndexExitShort(self):
        '''平仓到当前K线数量-空'''
        return self._lastOrder.IndexExitShort

    @property
    def Position(self):
        '''持仓净头寸'''
        return self.PositionLong - self.PositionShort

    @property
    def CurrentBar(self):
        '''当前K线序号(0开始)'''
        return max(len(self.Bars) - 1, 0)

    def on_tick(self, tick: Tick):
        self.ticks.append(tick)
        self.tick_update(tick)

    def on_min(self, bar: Bar):
        '''分笔数据处理'''
        if len(self.Bars) == 0 or self.Bars[-1].D != bar.D:  # 新数据
            self.__new_min_bar__(bar)  # 新K线数据插入
        else:
            self.__update_bar__(bar)

    def __new_min_bar__(self, bar2):
        """有新min_bar添加"""
        bar = copy.copy(bar2)
        bar_time = time.strptime(bar.D, "%Y-%m-%d %H:%M:%S")
        year = bar_time.tm_year
        mon = bar_time.tm_mon
        day = bar_time.tm_mday
        hour = bar_time.tm_hour
        mins = bar_time.tm_min
        if self.IntervalType == IntervalType.Minute:
            mins = bar_time.tm_min // self.Interval * self.Interval
        elif self.IntervalType == IntervalType.Hour:
            hour = hour // self.Interval
            mins = 0
        elif self.IntervalType == IntervalType.Day:
            hour = 0
            mins = 0
        elif self.IntervalType == IntervalType.Month:
            hour = 0
            mins = 0
            day = 1
        elif self.IntervalType == IntervalType.Year:
            hour = 0
            mins = 0
            day = 1
            mon = 1
        elif self.IntervalType == IntervalType.Week:
            hour = 0
            mins = 0
            # 用周号替换日期
            day = time.strftime('%W', bar_time)

        # time -> str
        bar_time = f'{year}-{mon:02d}-{day:02d} {hour:02d}:{mins:02d}:00'
        if len(self.Bars) == 0 or self.Bars[-1].D != bar_time:
            bar.D = bar_time
            self.Bars.append(bar)

            self.D.append(bar.D)
            self.H.append(bar.H)
            self.L.append(bar.L)
            self.O.append(bar.O)
            self.C.append(bar.C)
            self.V.append(bar.V)
            self.I.append(bar.I)
        else:
            old_bar = self.Bars[-1]
            self.H[-1] = old_bar.H = max(bar.H, old_bar.H)
            self.L[-1] = old_bar.L = min(bar.L, old_bar.L)
            self.C[-1] = old_bar.C = bar.C
            old_bar.V += bar.V
            self.V[-1] = old_bar.V
            self.I[-1] = old_bar.I = bar.I
            # bar.A = tick.AveragePrice

        self.bar_update(self, bar)

    def __update_bar__(self, bar):
        """更新当前数据序列"""

        self.D[-1] = bar.D
        self.H[-1] = bar.H
        self.L[-1] = bar.L
        self.O[-1] = bar.O
        self.C[-1] = bar.C
        self.V[-1] = bar.V
        self.I[-1] = bar.I
        self.bar_update(self, bar)

    def __order__(self, direction, offset, price, volume, remark):
        """策略执行信号"""
        # if self.SingleOrderOneBar:
        #     # 平仓后允许开仓
        #     if self.ExitDateShort == self.D[-1] and (not (direction == DirectType.Buy and offset == OffsetType.Open)):
        #         return
        #     if self.ExitDateLong == self.D[-1] and (not (direction == DirectType.Sell and offset == OffsetType.Open)):
        #         return
        #     if self.LastEntryDateLong == self.D[-1] or self.LastEntryDateShort == self.D[-1]:
        #         return
        if self.SingleOrderOneBar and (self.LastEntryDateLong == self.D[-1]
                                       or self.LastEntryDateShort == self.D[-1]
                                       or self.ExitDateLong == self.D[-1]
                                       or self.ExitDateShort == self.D[-1]):
            return
        order = OrderItem()
        order.Instrument = self.Instrument
        order.DateTime = self.D[-1]
        order.Direction = direction
        order.Offset = offset
        order.Price = price
        order.Volume = volume
        order.Remark = remark

        self.Orders.append(order)

        # 处理策略相关属性
        order.IndexEntryLong = self._lastOrder.IndexEntryLong
        order.IndexExitLong = self._lastOrder.IndexExitLong
        order.IndexEntryShort = self._lastOrder.IndexEntryShort
        order.IndexExitShort = self._lastOrder.IndexExitShort
        order.IndexLastEntryLong = self._lastOrder.IndexLastEntryLong
        order.IndexLastEntryShort = self._lastOrder.IndexLastEntryShort
        order.AvgEntryPriceLong = self._lastOrder.AvgEntryPriceLong
        order.AvgEntryPriceShort = self._lastOrder.AvgEntryPriceShort
        order.PositionLong = self._lastOrder.PositionLong
        order.PositionShort = self._lastOrder.PositionShort
        order.EntryDateLong = self._lastOrder.EntryDateLong
        order.EntryDateShort = self._lastOrder.EntryDateShort
        order.EntryPriceLong = self._lastOrder.EntryPriceLong
        order.EntryPriceShort = self._lastOrder.EntryPriceShort
        order.ExitDateLong = self._lastOrder.ExitDateLong
        order.ExitDateShort = self._lastOrder.ExitDateShort
        order.ExitPriceLong = self._lastOrder.ExitPriceLong
        order.ExitPriceShort = self._lastOrder.ExitPriceShort
        order.LastEntryDateLong = self._lastOrder.LastEntryDateLong
        order.LastEntryDateShort = self._lastOrder.LastEntryDateShort
        order.LastEntryPriceLong = self._lastOrder.LastEntryPriceLong
        order.LastEntryPriceShort = self._lastOrder.LastEntryPriceShort

        diroff = '{0}-{1}'.format(order.Direction.name, order.Offset.name)
        if diroff == 'Buy-Open':
            if self._lastOrder.PositionLong == 0:
                order.IndexEntryLong = len(self.Bars) - 1
                order.EntryDateLong = self.D[-1]  # str '20160630 21:25:00'
                order.EntryPriceLong = order.Price
                order.PositionLong = order.Volume
                order.AvgEntryPriceLong = order.Price
            else:
                order.PositionLong += order.Volume
                order.AvgEntryPriceLong = (
                                                  self._lastOrder.PositionLong * self._lastOrder.AvgEntryPriceLong + order.Volume * order.Price) / (
                                                  self._lastOrder.PositionLong + order.Volume)
            order.IndexLastEntryLong = len(self.Bars) - 1
            order.LastEntryPriceLong = order.Price
            order.LastEntryDateLong = self.D[-1]
        elif diroff == 'Buy-Close':
            c_lots = min(self._lastOrder.PositionShort, order.Volume)  # 能够平掉的数量
            if c_lots > 0:  # 避免无仓可平
                order.PositionShort -= c_lots

                order.IndexExitShort = len(self.Bars) - 1
                order.ExitDateShort = self.D[-1]
                order.ExitPriceShort = order.Price
                # if order.PositionShort == 0:
                # order.AvgEntryPriceShort = 0  # 20180906注销方便后期计算盈利
        elif diroff == 'Sell-Open':
            if self._lastOrder.PositionShort == 0:
                order.IndexEntryShort = len(self.Bars) - 1
                order.EntryDateShort = self.D[-1]  # time or double or str ???
                order.EntryPriceShort = order.Price
                order.AvgEntryPriceShort = order.Price
                order.PositionShort = order.Volume
            else:
                order.PositionShort += order.Volume
                order.AvgEntryPriceShort = (
                                                   self._lastOrder.PositionShort * self._lastOrder.AvgEntryPriceShort + order.Volume * order.Price) / (
                                                   self._lastOrder.PositionShort + order.Volume)
            order.IndexLastEntryShort = len(self.Bars) - 1
            order.LastEntryPriceShort = order.Price
            order.LastEntryDateShort = self.D[-1]
        elif diroff == 'Sell-Close':
            c_lots = min(self._lastOrder.PositionLong, order.Volume)  # 能够平掉的数量
            if c_lots > 0:  # 避免无仓可平
                order.PositionLong -= c_lots

                order.IndexExitLong = len(self.Bars) - 1
                order.ExitDateLong = self.D[-1]
                order.ExitPriceLong = order.Price
                # if order.PositionLong == 0:
                # order.AvgEntryPriceLong = 0  # 20180906注销方便后期计算盈利

        self._lastOrder = order
        self.stra_onorder(self, order)

    def Buy(self, price: float, volume: int, remark: str = ''):
        """买开"""
        self.__order__(DirectType.Buy, OffsetType.Open, price, volume, remark)

    def Sell(self, price, volume, remark: str = ''):
        """买平"""
        self.__order__(DirectType.Sell, OffsetType.Close, price, volume,
                       remark)

    def SellShort(self, price, volume, remark: str = ''):
        """卖开"""
        self.__order__(DirectType.Sell, OffsetType.Open, price, volume, remark)

    def BuyToCover(self, price, volume, remark: str = ''):
        """买平"""
        self.__order__(DirectType.Buy, OffsetType.Close, price, volume, remark)


class StraConf:
    def __init__(self) -> None:
        self.stra_class=''
        self.id=0
        self.name=""
        self.start_dt=''
        self.end_dt=""
        self.data=[['symbol',0,1],]
        self.params={}
        
    